﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;

using SharedGenomics.Workbench.Models;

namespace SharedGenomics.Workbench.Tests.Models
{
    [TestClass]
    public class WatchListModelTests
    {
        #region Private Members

        private WatchListModel model;

        #endregion

        #region Setup

        [TestInitialize]
        public void Setup()
        {
            this.model = new WatchListModel();
        }

        #endregion

        [TestMethod]
        public void ConstructorTest()
        {
            WatchListModel model = new WatchListModel();
            Assert.IsNotNull(model.MarkerComparer);
            Assert.AreEqual(0, model.Items.Count());
        }

        [TestMethod]
        public void LoadIdentifiersTest_Null()
        {
            this.model.LoadIdentifiers(null);
            Assert.AreEqual(0, this.model.Items.Count());
        }

        [TestMethod]
        public void LoadIdentifiersTest_Unique()
        {
            this.model.LoadIdentifiers(new[] { "id1", "id2", "id3" });
            Assert.AreEqual(3, this.model.Items.Count());
            Assert.AreEqual(0, this.model.Items.Where(wi => wi.IsFlagged).Count());
        }

        [TestMethod]
        public void LoadIdentifiersTest_ContainsDuplicates()
        {
            this.model.LoadIdentifiers(new[] { "id1", "id1", "id2" });
            Assert.AreEqual(2, this.model.Items.Count());
        }

        [TestMethod]
        public void LoadIdentifiersTest_ContainsDuplicates_CaseInsensitive()
        {
            this.model.LoadIdentifiers(new[] { "id1", "ID1", "id2" });
            Assert.AreEqual(2, this.model.Items.Count());
        }

        [TestMethod]
        public void LoadIdentifiersTest_ContainsNull()
        {
            this.model.LoadIdentifiers(new[] { "id1", null, "id2" });
            Assert.AreEqual(2, this.model.Items.Count());
        }

        [TestMethod]
        public void LoadIdentifiersTest_RaisesListChangedEvent()
        {
            this.AssertListChangedEventRaised(() =>
            {
                this.model.LoadIdentifiers(new[] { "id1", "id2" });
            });
        }

        [TestMethod]
        public void ClearIdentifiersTest()
        {
            this.model.LoadIdentifiers(new[] { "id1", "id2" });
            this.model.ClearIdentifiers();

            Assert.AreEqual(0, this.model.Items.Count());
        }

        [TestMethod]
        public void ClearIdentifiersTest_RaisesListChangedEvent()
        {
            this.AssertListChangedEventRaised(() => 
            {
                this.model.ClearIdentifiers();
            });
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void SyncWithTest_MapFuncNull()
        {
            this.model.SyncWith<string>(new string[] { }, null);
        }

        [TestMethod]
        public void SyncWithTest_SingleItemInData()
        {
            this.model.LoadIdentifiers(new[] { "id1", "id2" });
            this.model.SyncWith<string>(new[] { "id2" }, s => s);

            //there should be two items in the list - one flagged (id2) and the other not flagged
            Assert.AreEqual(2, this.model.Items.Count());
            Assert.IsFalse(this.model.Items.Where(li => li.Name == "id1").Single().IsFlagged);
            Assert.IsTrue(this.model.Items.Where(li => li.Name == "id2").Single().IsFlagged);
        }

        [TestMethod]
        public void SyncWithTest_ExtraItemsInData()
        {
            this.model.LoadIdentifiers(new[] { "id1", "id2" });
            this.model.SyncWith<string>(new[] { "id2", "id3", "id4" }, s => s);

            //there should still be two items in the list - id2 should be flagged as it is in the data
            Assert.AreEqual(2, this.model.Items.Count());
            Assert.IsTrue(this.model.Items.Where(li => li.Name == "id2").Single().IsFlagged);
        }

        [TestMethod]
        public void SyncWithTest_DataNull()
        {
            this.model.LoadIdentifiers(new[] { "id1", "id2" });
            this.model.SyncWith<string>(null, s => s);

            Assert.AreEqual(2, this.model.Items.Count());
            Assert.AreEqual(2, this.model.Items.Where(li => !li.IsFlagged).Count());
        }

        [TestMethod]
        public void SyncWithTest_RaisesListChangedEvent()
        {
            this.model.LoadIdentifiers(new[] { "id1", "id2" });
            this.AssertListChangedEventRaised(() =>
            {
                this.model.SyncWith<string>(new[] { "id2" }, s => s);
            });
        }

        [TestMethod]
        public void MarkerComparerTest_Null()
        {
            this.model.MarkerComparer = null;
            Assert.IsNotNull(this.model.MarkerComparer);
        }

        [TestMethod]
        public void MarkerComparerTest_NotNull()
        {
            var comparer = StringComparer.CurrentCulture;
            this.model.MarkerComparer = comparer;
            Assert.AreEqual(comparer, this.model.MarkerComparer);
        }

        private void AssertListChangedEventRaised(Action act)
        {
            bool eventRaised = false;

            this.model.ListChanged += (o, e) => { eventRaised = true; };
            act();

            Assert.IsTrue(eventRaised);
        }
    }
}
